import pathlib, os, shutil

from uuid import UUID

from PyQt5.QtWidgets import QVBoxLayout, QLabel, QLineEdit, QComboBox, QListWidget, QListWidgetItem, QWidget
from PyQt5.QtCore import Qt, pyqtSignal

from data import database, config, get_cover

class TrackEditor(QWidget):
    track_updated = pyqtSignal(UUID)

    COMBO_BOX_UUID_ROLE = 1001

    def __init__(self):
        super().__init__(None)
        self.setFixedWidth(270)

        self.uuid: UUID = None

        #region
        layout = QVBoxLayout()

        #region uuid
        self.uuid_label = QLabel()
        self.uuid_label.setText('Track UUID')
        layout.addWidget(self.uuid_label)

        self.uuid_view = QLineEdit()
        self.uuid_view.setReadOnly(True)
        layout.addWidget(self.uuid_view)
        #endregion

        #region path
        self.path_label = QLabel()
        self.path_label.setText('Track Path')
        layout.addWidget(self.path_label)

        self.path_edit = QLineEdit()
        self.path_edit.editingFinished.connect(self.path_editing_finished)
        layout.addWidget(self.path_edit)
        #endregion

        #region title
        self.title_label = QLabel()
        self.title_label.setText('Track Title')
        layout.addWidget(self.title_label)

        self.title_edit = QLineEdit()
        self.title_edit.setPlaceholderText('Enter track title here')
        self.title_edit.editingFinished.connect(self.track_title_changed)
        layout.addWidget(self.title_edit)
        #endregion

        #region artists
        self.artists_label = QLabel()
        self.artists_label.setText('Track Artists')
        layout.addWidget(self.artists_label)

        self.artist_list = QListWidget()
        self.artist_list_items: dict[UUID, QListWidgetItem] = {}
        self.artist_list.itemChanged.connect(self.track_artists_changed)
        layout.addWidget(self.artist_list)
        #endregion

        #region album
        self.album_label = QLabel()
        self.album_label.setText('Track Album')
        layout.addWidget(self.album_label)

        self.album_edit = QComboBox()
        self.album_edit.activated.connect(self.track_album_changed)
        layout.addWidget(self.album_edit)
        #endregion

        #region genre
        self.genre_label = QLabel()
        self.genre_label.setText('Track Genre')
        layout.addWidget(self.genre_label)

        self.genre_edit = QComboBox()
        self.genre_edit.activated.connect(self.track_genre_changed)
        layout.addWidget(self.genre_edit)
        #endregion

        #region cover
        self.cover_label = QLabel()
        self.cover_label.setText('Track Cover')
        layout.addWidget(self.cover_label)

        self.cover_edit = QComboBox()
        self.cover_edit.activated.connect(self.track_cover_changed)
        layout.addWidget(self.cover_edit)

        self.cover_preview_label = QLabel()
        self.cover_preview_label.setDisabled(True)
        layout.addWidget(self.cover_preview_label)
        #endregion

        self.setLayout(layout)
        #endregion

    def track_title_changed(self):
        text = self.title_edit.text()
        database['music'][self.uuid]['title'] = text
        self.track_updated.emit(self.uuid)

    def refresh_album_edit(self):
        self.album_edit.clear()
        self.album_edit.addItem('Unknown Album')
        current_index = 0
        for uuid in sorted(database['albums'], key=lambda x: str(database['albums'][x]['name']) if database['albums'][x]['name'] else str(uuid)):
            self.album_edit.addItem((database['albums'][uuid]['name'] if database['albums'][uuid]['name'] else str(uuid)) + ' by ' + (database['artists'][database['albums'][uuid]['artist']]['name'] if database['albums'][uuid]['artist'] else 'Unknown'))
            index = self.album_edit.count() - 1
            self.album_edit.setItemData(index, uuid, self.COMBO_BOX_UUID_ROLE)
            if uuid == database['music'][self.uuid]['album']:
                current_index = index
        self.album_edit.setCurrentIndex(current_index)

    def track_album_changed(self):
        uuid = self.album_edit.currentData(self.COMBO_BOX_UUID_ROLE)
        if uuid:
            database['music'][self.uuid]['album'] = UUID(uuid) if not isinstance(uuid, UUID) else uuid
        else:
            database['music'][self.uuid]['album'] = None
        self.track_updated.emit(self.uuid)

    def track_artists_changed(self):
        database['music'][self.uuid]['artists'].clear()
        for uuid in self.artist_list_items:
            item = self.artist_list_items[uuid]
            if item.checkState():
                database['music'][self.uuid]['artists'].append(uuid)
        self.track_updated.emit(self.uuid)

    def path_editing_finished(self):
        input_path = pathlib.Path(self.path_edit.text())
        lib_path = pathlib.Path(config['files']['audio']['path'])
        track_path = database['music'][self.uuid]['path']
        if input_path.is_absolute():
            try:
                input_path = input_path.relative_to(lib_path)
            except ValueError as e:
                print('Error:', ValueError.__name__ + ':', e)
                input_path = pathlib.Path(track_path)
        final_path = str(input_path)
        final_path = final_path.replace('\\', '/')
        for char in '<>:"|?*':
            final_path = final_path.replace(char, '')
        final_path_parts: list[str] = final_path.split('/')
        final_path = ''
        for part in final_path_parts:
            part = part.strip()
            part = part.strip('.')
            final_path += (part + '/')
        final_path = final_path.strip('/')
        self.path_edit.setText(str(input_path))
        if not os.path.exists(pathlib.Path(lib_path, final_path).parent):
            os.makedirs(pathlib.Path(lib_path, final_path).parent)
        shutil.move(
            os.path.join(lib_path, track_path),
            os.path.join(lib_path, final_path)
        )
        database['music'][self.uuid]['path'] = final_path
        self.path_edit.setText(final_path)
        self.track_updated.emit(self.uuid)

    def refresh_genre_edit(self):
        self.genre_edit.clear()
        self.genre_edit.addItem('Unknown Genre')
        current_index = 0
        for uuid in sorted(database['genres'], key=lambda x: str(database['genres'][x]['name']) if database['genres'][x]['name'] else 'Unknown'):
            self.genre_edit.addItem(database['genres'][uuid]['name'] if database['genres'][uuid]['name'] else uuid)
            index = self.genre_edit.count() - 1
            self.genre_edit.setItemData(index, uuid, self.COMBO_BOX_UUID_ROLE)
            if uuid == database['music'][self.uuid]['genre']:
                current_index = index
        self.genre_edit.setCurrentIndex(current_index)

    def track_genre_changed(self):
        uuid = self.genre_edit.currentData(self.COMBO_BOX_UUID_ROLE)
        if uuid:
            database['music'][self.uuid]['genre'] = UUID(uuid) if not isinstance(uuid, UUID) else uuid
        else:
            database['music'][self.uuid]['genre'] = None
        self.track_updated.emit(self.uuid)

    def refresh_cover_edit(self):
        self.cover_edit.clear()
        self.cover_edit.addItem('No Cover')
        current_index = 0
        for uuid in sorted(database['covers'], key=lambda x: str(database['covers'][x]['name']) if database['covers'][x]['name'] else str(uuid)):
            self.cover_edit.addItem(database['covers'][uuid]['name'] if database['covers'][uuid]['name'] else str(uuid))
            index = self.cover_edit.count() - 1
            self.cover_edit.setItemData(index, uuid, self.COMBO_BOX_UUID_ROLE)
            if uuid == database['music'][self.uuid]['cover']:
                current_index = index
        self.cover_edit.setCurrentIndex(current_index)
        self.track_cover_changed()

    def track_cover_changed(self):
        uuid = self.cover_edit.currentData(self.COMBO_BOX_UUID_ROLE)
        if uuid:
            database['music'][self.uuid]['cover'] = UUID(uuid) if not isinstance(uuid, UUID) else uuid
            self.cover_preview_label.setEnabled(True)
            cover_label_pixmap = get_cover(uuid)
            cover_label_pixmap = cover_label_pixmap.scaledToWidth(256)
            self.cover_preview_label.setPixmap(cover_label_pixmap)
        else:
            database['music'][self.uuid]['cover'] = None
            self.cover_preview_label.setEnabled(False)
        self.track_updated.emit(self.uuid)

    def refresh_artist_edit(self):
        self.artist_list.clear()
        self.artist_list_items = {}
        for uuid in sorted(database['artists'], key=lambda x: str(database['artists'][x]['name']) if database['artists'][x]['name'] else 'No Name'):
            if uuid in self.artist_list_items:
                continue
            artist = database['artists'][uuid]
            item = QListWidgetItem(artist['name'] if artist['name'] else 'No Name')
            item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
            self.artist_list_items[uuid] = item
            if uuid in database['music'][self.uuid]['artists']:
                self.artist_list_items[uuid].setCheckState(Qt.Checked)
            else:
                self.artist_list_items[uuid].setCheckState(Qt.Unchecked)
            self.artist_list.addItem(item)

    def load(self, uuid: UUID):
        self.uuid = uuid

        self.setDisabled(False)

        self.uuid_view.setText(str(self.uuid))
        self.path_edit.setText(database['music'][self.uuid]['path'])
        self.title_edit.setText(database['music'][self.uuid]['title'])
        self.refresh_album_edit()
        self.refresh_genre_edit()
        self.refresh_cover_edit()
        self.refresh_artist_edit()
